import 'package:flutter/material.dart';

class LazyIndexedStack extends StatefulWidget {
  final LazyIndexedStackController controller;
  const LazyIndexedStack({super.key, required this.controller});
  @override
  State<StatefulWidget> createState() => _LazyIndexedStackState();
}

class _LazyIndexedStackState extends State<LazyIndexedStack> {
  @override
  void initState() {
    super.initState();
    widget.controller._bind(this);
    widget.controller._init();
  }

  @override
  void dispose() {
    super.dispose();
    widget.controller._dispose();
  }

  /// 将构建视图委托给 Controller 去处理
  @override
  Widget build(BuildContext context) => widget.controller._build();

  /// 提供给 Controller 让它具备重绘的功能
  void _rebuild([VoidCallback? callback]) => setState(callback ?? () {});
}

class LazyIndexedStackController {
  // 传入进来的原始组件，目的仅仅是为了去替换列表中的占位组件
  // 当切换到对应的视图进行展示时，会将对应列表中的组件复制到 _items 中，当全部后备替换完成时代表所有都被正常载入了。
  final List<Widget> _children;
  // 用于真正显示的组件（默认情况下全是占位组件：SizedBox.shrink()）
  final List<Widget> _items;

  // State 引用，有了它可以唤起 State 进行重绘
  _LazyIndexedStackState? _state;
  // 用于记录当前激活的视图下标
  int _index = 0;

  LazyIndexedStackController({required List<Widget> children, int index = 0})
    : _index = index,
      _children = children,
      _items = List.generate(children.length, (i) => const SizedBox.shrink());

  // 更改当前显示的视图下标
  void changeIndex(int index) {
    assert(
      index >= 0 && index <= _children.length,
      "Index out of range(0..${_children.length}) $index",
    );
    _replaceView(_index = index);
    _state?._rebuild();
  }

  /// * 不对外提供
  /// 绑定 State 到当前 Controller 中
  void _bind(_LazyIndexedStackState state) => _state = state;

  /// * 不对外提供
  /// 初始化默认显示的视图
  void _init() => _replaceView(_index);

  /// * 不对外提供
  /// 构建当前视图
  Widget _build() => IndexedStack(index: _index, children: _items);

  /// * 不对外提供
  // 将占位组件替换为真正的视图组件对象
  void _replaceView(int index) {
    if (_items[index] is SizedBox) {
      _items[index] = _children[index];
    }
  }

  /// * 不对外提供
  /// 销毁时调用以清除列表数据
  void _dispose() {
    _state = null;
    _items.clear();
    _children.clear();
  }
}
